{"componentChunkName":"component---src-templates-tags-js","path":"/tags/progressiveweb apps/","result":{"data":{"allMarkdownRemark":{"totalCount":1,"edges":[{"node":{"id":"fcd14508-a04f-57f7-9c1c-93a84b8bcf94","html":"<p><a href=\"https://developers.google.com/web/progressive-web-apps/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Progressive Web Applications (PWA)</a> are getting more and more attractions by day. With recent move from DevKit team, adding service worker support to the latest version of Safari, and <a href=\"https://www.windowscentral.com/first-batch-windows-10-progressive-web-apps-here\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Microsoft adding progressive web applications to their store</a>, we can see the importance of the key role PWAs play in today’s market.</p>\n<!--more-->\n<p>With <a href=\"https://angular.io/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Angular version 5.0.0</a>, the team introduced a new version of their <a href=\"https://angular.io/guide/service-worker-intro\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">service worker aka (ngsw)</a> which has a ton of goodies from a fully functional service worker to full support for push notifications. In this post I will iterate through them and turn an existing application into a PWA.</p>\n<p>The demo application I’ve prepared can be found on <a href=\"https://github.com/yashints/Angular-PWA\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">my GitHub repo here</a>.</p>\n<h2 id=\"table-of-content\" style=\"position:relative;\"><a href=\"#table-of-content\" aria-label=\"table of content permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Table of content</h2>\n<ol>\n<li><a href=\"#wispwa\">What is a progressive web application?</a></li>\n<li><a href=\"#support\">Current state of support in major browsers</a></li>\n<li><a href=\"#keycomponents\">Key components</a></li>\n<li><a href=\"#lighthouse\">How to evaluate your current application?</a></li>\n<li><a href=\"#appmanifest\">Application manifest</a></li>\n<li><a href=\"#angularpwa\">Angular and PWA</a></li>\n<li><a href=\"#ngsw\">Angular Service Worker aka (ngsw)</a></li>\n<li><a href=\"#hood\">Under the hood</a></li>\n<li><a href=\"#push\">Push notifications</a></li>\n</ol>\n<h2 id=\"a-idwispwaawhat-is-a-progressive-web-application\" style=\"position:relative;\"><a href=\"#a-idwispwaawhat-is-a-progressive-web-application\" aria-label=\"a idwispwaawhat is a progressive web application permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"wispwa\"></a>What is a progressive web application?</h2>\n<p>In 2015, designer <strong>Frances Berriman</strong> and <a href=\"https://en.wikipedia.org/wiki/Google_Chrome\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Google Chrome</a> engineer <strong>Alex Russell</strong> coined the term <code class=\"language-text\">progressive web apps</code>.  These are applications which leverage new features supported by modern browsers such as <a href=\"https://developer.mozilla.org/en-US/docs/Web/Manifest\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">application manifest</a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">service worker</a> allowing us to create faster, more engaging and reliable applications.</p>\n<p>They have the following characteristics:</p>\n<ul>\n<li><strong>Progressive</strong> - Work for every user, regardless of browser choice because they’re built with <a href=\"https://en.wikipedia.org/wiki/Progressive_enhancement\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">progressive enhancement</a> as a core tenet.</li>\n<li><strong>Responsive</strong> - Fit any form factor: desktop, mobile, tablet, or forms yet to emerge.</li>\n<li><strong>Connectivity independent</strong> - <a href=\"https://en.wikipedia.org/wiki/Progressive_web_app#Service_Workers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Service workers</a> allow work offline, or on low quality networks.</li>\n<li><strong>App-like</strong> - Feel like an app to the user with app-style interactions and navigation.</li>\n<li><strong>Fresh</strong> - Always up-to-date thanks to the service worker update process.</li>\n<li><strong>Safe</strong> - Served via HTTPS to prevent snooping and ensure content hasn’t been tampered with.</li>\n<li><strong>Discoverable</strong> - Are identifiable as “applications” thanks to W3C manifests<sup><a href=\"https://en.wikipedia.org/wiki/Progressive_web_app#cite_note-w3cmanifest-6\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">[6]</a></sup> and service worker registration scope allowing search engines to find them.</li>\n<li><strong>Re-engageable</strong> - Make re-engagement easy through features like <a href=\"https://en.wikipedia.org/wiki/Push_technology\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">push notifications</a>.</li>\n<li><strong>Installable</strong> - Allow users to “keep” apps they find most useful on their home screen without the hassle of an app store.</li>\n<li><strong>Linkable</strong> - Easily shared via a URL and do not require complex installation.\r\nThese applications look very similar to a native mobile application and offer some features that makes user very happy. The most important one is ability to add the application to your device’s home screen. Accessing device’s hardware like location, camera and etc. is another thing where it gives a native experience to users.</li>\n</ul>\n<h2 id=\"a-idkeycomponentsakey-components\" style=\"position:relative;\"><a href=\"#a-idkeycomponentsakey-components\" aria-label=\"a idkeycomponentsakey components permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"keycomponents\"></a>Key components</h2>\n<p>There are three key components you will have to add to your project to be able to say your web application is progressive.</p>\n<h3 id=\"application-manifest\" style=\"position:relative;\"><a href=\"#application-manifest\" aria-label=\"application manifest permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Application manifest</h3>\n<p>From <a href=\"https://developer.mozilla.org/en-US/docs/Web/Manifest\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Mozilla documents</a>:</p>\n<blockquote>\n<p>The web app manifest provides information about an application (such as name, author, icon, and description) in a <code class=\"language-text\">JSON</code> text file. The purpose of the manifest is to install web applications to the homescreen of a device, providing users with quicker access and a richer experience.</p>\n</blockquote>\n<h3 id=\"service-worker\" style=\"position:relative;\"><a href=\"#service-worker\" aria-label=\"service worker permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Service worker</h3>\n<p>A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction. It includes offline support, request caching, <a href=\"https://developers.google.com/web/updates/2015/03/push-notifications-on-the-open-web\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">push notifications</a> and <a href=\"https://developers.google.com/web/updates/2015/12/background-sync\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">background sync</a>.</p>\n<h3 id=\"app-shell\" style=\"position:relative;\"><a href=\"#app-shell\" aria-label=\"app shell permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>App shell</h3>\n<p>An <strong>application shell</strong> (or app shell) architecture is one way to build a Progressive Web App that reliably and instantly loads on your users’ screens, similar to what you see in native applications.</p>\n<h2 id=\"a-idsupportacurrent-state-of-support-in-major-browsers\" style=\"position:relative;\"><a href=\"#a-idsupportacurrent-state-of-support-in-major-browsers\" aria-label=\"a idsupportacurrent state of support in major browsers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"support\"></a>Current state of support in major browsers</h2>\n<p>Most of the major browsers have been trying to add support for application manifest and service workers. By the start of the last year <a href=\"https://www.google.ca/chrome/browser/desktop/index.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Chrome</a> and <a href=\"https://play.google.com/store/apps/details?id=com.android.chrome&#x26;hl=en\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Chrome mobile</a> had full support, whereas <a href=\"https://www.apple.com/au/safari/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Safari</a> wasn’t even looking into it. <a href=\"https://www.microsoft.com/en-au/windows/microsoft-edge\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Microsoft Edge</a> not only were working on it, but also looking into adding PWA’s to their store with the help of <a href=\"https://electronjs.org\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">ElectronJS</a>. Mozilla’s <a href=\"https://www.mozilla.org/en-US/firefox/new\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FireFox</a> team was supporting most of the features as well but they were a bit behind <a href=\"https://www.google.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Google</a> on that.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/579975b09e1d944583efe49ea3a50784/00d43/pwasupport2016.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 12.962962962962962%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwklEQVR42gG3AEj/AGJsVXBqUm97UoqbXYKNVH2GXYWWWLe3ptjW2JSejF1lSGVxVJmJUqGRSJOMbqmaacLAqtTU2bq7t5acmQBQekeGfm+fnT+Hb1CbpkqffkSIilnMzqrMztqgmIViLxeRej32u3j/xka1jrKyid3k3bDIytLp6OiBrNYAdJ1ydqd6pbNvuphQvrlcwa1cuqNS2d7F4d7jr7WhdW5TrKpy/sl4/s1m38Wg0rWq6eO+1tfb7OrmwtLi/cZthwzG6RcAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"PWA support 2016\"\n        title=\"\"\n        src=\"/static/579975b09e1d944583efe49ea3a50784/00d43/pwasupport2016.png\"\n        srcset=\"/static/579975b09e1d944583efe49ea3a50784/01bf6/pwasupport2016.png 270w,\n/static/579975b09e1d944583efe49ea3a50784/07484/pwasupport2016.png 540w,\n/static/579975b09e1d944583efe49ea3a50784/00d43/pwasupport2016.png 1000w\"\n        sizes=\"(max-width: 1000px) 100vw, 1000px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>Later in the year there was a massive effort put into supporting the new APIs and surprisingly Apple started working on it seriously. Microsoft Edge got it in preview and Safari added some basic support as well.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/d8bdd16fdb6e0488bcc1658019525f35/00d43/pwasupport2017.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 12.222222222222221%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVR42gF6AIX/AHp5XnJuTG9uWH5zSomRV4eVW4WEUYiZZcnGusXKyXBiSGNcPqWTWbOaSXFsj4aBjI2lcquxdaqlT6qsawBmmniCmWZhk2+OpWqsjkSqn0u0mESnmlXa3c7N0NJ3Y0h5bELrw2/lvWSCfLSblK+Suou6zIvMy1/DyXywKkFvZdf/awAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"PWA support 2017\"\n        title=\"\"\n        src=\"/static/d8bdd16fdb6e0488bcc1658019525f35/00d43/pwasupport2017.png\"\n        srcset=\"/static/d8bdd16fdb6e0488bcc1658019525f35/01bf6/pwasupport2017.png 270w,\n/static/d8bdd16fdb6e0488bcc1658019525f35/07484/pwasupport2017.png 540w,\n/static/d8bdd16fdb6e0488bcc1658019525f35/00d43/pwasupport2017.png 1000w\"\n        sizes=\"(max-width: 1000px) 100vw, 1000px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<blockquote>\n<h6 id=\"photo-from-httpsispwareadytoxicjohanncom\" style=\"position:relative;\"><a href=\"#photo-from-httpsispwareadytoxicjohanncom\" aria-label=\"photo from httpsispwareadytoxicjohanncom permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Photo from : <a href=\"https://ispwaready.toxicjohann.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://ispwaready.toxicjohann.com</a></h6>\n</blockquote>\n<h2 id=\"a-idlighthouseahow-to-evaluate-your-current-application\" style=\"position:relative;\"><a href=\"#a-idlighthouseahow-to-evaluate-your-current-application\" aria-label=\"a idlighthouseahow to evaluate your current application permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"lighthouse\"></a>How to evaluate your current application?</h2>\n<p>Your question now might be where to start. There are some tools that allow you to evaluate your application against the PWA requirements. However the most easy to use is <a href=\"https://developers.google.com/web/tools/lighthouse/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Lighthouse</a> which is now built in <a href=\"https://developer.chrome.com/devtools\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Chrome DevTools</a>.</p>\n<p>If you open up your developer tools and go to Audits tab, you will see its logo in the middle of the page and there is an audit button which you can click to get started with your application.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 602px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/8723f124fef37585cdf45c604147a4c8/32056/lighthouse.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 67.77777777777777%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdElEQVR42qWSTUrEMBTH5wieYE7g2o0H8AYyICiC4EIdGBDdiAdw50oUdDZuXSgoiuNWFBXRTUVFLLZNmqadtmNnpm1eYvoxgjhf4j+lCS/vl5fX/gsYY4tSwzAopQhhQoj2oVFqI4QMXQbtfAvL2bCTHMMk9E1FhDoFxmLG8pdUq9Vy3ToAJLE4zhbfAjnS1CiSExTEP9QH5okAOHAGf4SBSzo9Iz9pKDjJ4pEr2Kv36T5d3SunF4+qjMJQcFrEelEeppf1jZWjrcXtYknUbhIYYNC1sxvOr8PImJidU9cWnovjYrLM8i54bzjb8wM+VRETFa28vzlT3RtdVUpLgrp8AJyJAaeO8GLHEYrG0TtDvi9i1rfnOIoyY2TOCNvAQgGRYGHqjo6ToNP6D9jEWE/NiE2TENwOnDCw2w0SBo5meqpeD1LJAl3g1BSyrGwLLDeu1vxd+Zx7O2e+Stjvv93zazsNOLwOjm+bJ3fNg8sA17vAX2TVG5JB58YtAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lighthouse\"\n        title=\"\"\n        src=\"/static/8723f124fef37585cdf45c604147a4c8/32056/lighthouse.png\"\n        srcset=\"/static/8723f124fef37585cdf45c604147a4c8/01bf6/lighthouse.png 270w,\n/static/8723f124fef37585cdf45c604147a4c8/07484/lighthouse.png 540w,\n/static/8723f124fef37585cdf45c604147a4c8/32056/lighthouse.png 602w\"\n        sizes=\"(max-width: 602px) 100vw, 602px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>Even without doing anything you could browse your application and audit it to see how compliant your app is. When you run the report you will see a report like below and there are even hints on how to fix some of those.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 721px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f5a8ebf12e6bcd382cfc09b40397955c/01dae/score.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 70.37037037037037%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAABm0lEQVR42pVS25KdIBDk/38yR1cURFRm0FWu2+qm8nC2kkpXS40wl2YGUWsttSbSNez1RkxxZFNKfn75YOOn+hvrvtJBd1QRJWdm7ybFq035CnDsGtWEEB5vs5oP3ZVSrrwxSSPHZXyORDhPJvLbzt4/ATnnFJHnuzJssFz66nUSIogtVBVqGGTXyb5vm2YczbbthFzes+dt25y7bM+0uE3P5+zO55phUWWb78rs4ZcS0lfIS9+I1xfjs4SLOcZcbkBBzUmc56mVGvp+nme3OudW+NZ/ApViFHCdrDXGaDOO06SNsdbu+yf2zxDA8g4IxP55CoiapDRNo16vEdduW9O2sNe+X5Rah+Hu1BtiRPw9Kim56+ijc72kYSCsUgZrC3Mml4gT0cPoXFpdPg70BsoFRsIo8vrl1LCT42XB5I59R1K07o3xIiIxyCfYzzOD00Rak53QejyyvzTrujNko2G4s+06K7ul70kpB2odliVBMFNmn5h/SHDrEtvmjdZGKYY3hnRL+qPzscuPLa/iOD4dETPjKeF51f/BF063LK+/37xzAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lightouse score\"\n        title=\"\"\n        src=\"/static/f5a8ebf12e6bcd382cfc09b40397955c/01dae/score.png\"\n        srcset=\"/static/f5a8ebf12e6bcd382cfc09b40397955c/01bf6/score.png 270w,\n/static/f5a8ebf12e6bcd382cfc09b40397955c/07484/score.png 540w,\n/static/f5a8ebf12e6bcd382cfc09b40397955c/01dae/score.png 721w\"\n        sizes=\"(max-width: 721px) 100vw, 721px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>You can see how detail the report is and if you open those red items you will see what is missing.</p>\n<h2 id=\"a-idappmanifestaapplication-manifest\" style=\"position:relative;\"><a href=\"#a-idappmanifestaapplication-manifest\" aria-label=\"a idappmanifestaapplication manifest permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"appmanifest\"></a>Application manifest</h2>\n<p>Application manifest is a normal <code class=\"language-text\">JSON</code> file containing the initial flags and attributes a PWA needs. They vary from theme colour to short name which appears below the icon on your device home screen when you add it (amazingly it supports emoji’s).</p>\n<p>Theme colour tells the browser what colour to choose for background on address bar and toolbar. A sample is shown in below picture.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 894px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/0cdc81b7d96549c0e928b890d4c70dbc/38af3/themecolor.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 76.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAADKElEQVR42rWUa0iTYRTH32Xb3M2Z6eZ0NcPKDCKzYk5D0k2zQqEPdlmlFlg2r3NasxpkN4eXeYPKxCztQhZdqKiWBIKklNDF+SFzXyP6EOV96338905zvl2gTx14OB/O7/lxDud9Xor6H6HVaper1eqWqDVRDyIiIuxSqfQJl8t9yOSSnwhnlt0Ury7cGLP6flx05ONV4SF2uXj+I6WU1xauDFrqFaanp+8ymUqQazCQvPx8ZGdnE4PBgNTUVCdblklRvhsP2fo1pbcQmdNKFmc1ITTjAq3YYYNKk7bTKzQaC/ZMTLpAAy4A3zGT8dRuH2LKPNYwQsvNvv7mQaDyFVynevDd+hqTJ7qGkZRh3OOlUlKSD9iqrKg4c5I+Zi6FucRIqipOo8CQ4xFSjHu2S/+y4nznxYZKWMuPkLPHTagqN9P11hNI25qS7hUqN2zbJz38En7mN0Rk6oWgqIcIywYQsPu8k5oxTgvTKErin9HyXmwZhLD4JeEX9YJb3EeLCrsgT9i33StcGJellzW6sbQVRHN9CrE3CNHcAYIKnjjZy4uhKIG0uLs/rA2IbidEfW0KMR2gV54fhkhnYgv36oUVXxFQN0mC60YRWDNCJA2AMPv+tHBtE4Sx9yBhZhIIcjsdfo2ArHaEyGtHsaDOTfue/IQF2rwdXqGMEXJOD4NX7SaKhgkE2MYIZQMEjBCsLRsZIT/3ucNTC6gdJ8H1E+BW0zSn/DPkyYVzQmW8Xk9ZvkB73UU6HBNo7B0jPjUAb//dP4Q+B+0OYT1Q82KM3B4YR/xVN00d/YiQZFaHioS9en69C5ImkPArUwhtIYTbDIjzH/8i9IwsLul28Jiahwm/POW5Q/Orv0G+mdXhsnWJO3XGc9CWXnInmZrpBONF9+aj7Vi/vegD+8Nmwlez2/w2haklMoyOYXWlra7EggZExm2ZW4pMFrhLzPeBmMshYt68mczlwF8kGGI/vczMTF8/If+dmMeBaJrleFhawtyVBwbOCVUqVaRi8ZKOYKXKzjqdIcowy+9vOVS1pCxYGdb5k3nmyYpFYbcVKtWKv/0nOKzzr/gr9wPX/oFWpdI7FAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Theme colour\"\n        title=\"\"\n        src=\"/static/0cdc81b7d96549c0e928b890d4c70dbc/38af3/themecolor.png\"\n        srcset=\"/static/0cdc81b7d96549c0e928b890d4c70dbc/01bf6/themecolor.png 270w,\n/static/0cdc81b7d96549c0e928b890d4c70dbc/07484/themecolor.png 540w,\n/static/0cdc81b7d96549c0e928b890d4c70dbc/38af3/themecolor.png 894w\"\n        sizes=\"(max-width: 894px) 100vw, 894px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<blockquote>\n<h6 id=\"httpsdevelopersgooglecomwebupdates201508using-manifest-to-set-sitewide-theme-color------\" style=\"position:relative;\"><a href=\"#httpsdevelopersgooglecomwebupdates201508using-manifest-to-set-sitewide-theme-color------\" aria-label=\"httpsdevelopersgooglecomwebupdates201508using manifest to set sitewide theme color       permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>*<a href=\"https://developers.google.com/web/updates/2015/08/using-manifest-to-set-sitewide-theme-color*%C2%A0\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://developers.google.com/web/updates/2015/08/using-manifest-to-set-sitewide-theme-color* </a>            </h6>\n</blockquote>\n<p>As you can see in the right side, the background colour of the address bar is replaced with the application’s theme colour to maintain the consistency of the user experience and make it more interesting. Just note that this is only supported when you open your application from home screen.</p>\n<p>The most important attribute on application manifest is the <a href=\"https://developer.mozilla.org/en-US/docs/Web/Manifest#display\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">display mode</a>. This defines how your application is displayed when opened from home screen. There currently four modes in the specification:</p>\n<ul>\n<li><strong>fullscreen</strong>: All of the available display area is used and no user agent <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/chrome\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">chrome</a> is shown.</li>\n<li><strong>standalone</strong>: The application will look and feel like a standalone application. This can include the application having a different window, its own icon in the application launcher, etc. In this mode, the user agent will exclude UI elements for controlling navigation, but can include other UI elements such as a status bar.</li>\n<li><strong>minimal-ui</strong>: The application will look and feel like a standalone application, but will have a minimal set of UI elements for controlling navigation. The elements will vary by browser.</li>\n<li><strong>browser</strong>: The application opens in a conventional browser tab or new window, depending on the browser and platform. This is the default.\r\nTo get a better understanding of their difference have a look at the how the application is displayed in different modes. From left to right they are <code class=\"language-text\">minimum-ui</code>, <code class=\"language-text\">standalone</code>, and <code class=\"language-text\">full screen</code>.</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1080px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/e0046107a5c07e5f011f9686ba208635/105d8/launchfullscreen.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.29629629629629%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRElEQVR42jWRzWsTQRjG9w/x6tWDJy+CJ8FLwKzBQKxopRZLKniSQi+KKGqVgheh4ndTK6FWm/QjTa20TUub9NNWSprs5mProqlp0mR3Zt6Z3fFNQ4bhZfjN884884zyuGfyTttwt3/wdiAUvBzqUkOp+XzvlWjwQvjW+eEbZ0M3z33q8Y9tLZp9nSv93cnnXclH15bu+ROvejeVuxd/BE6NtZ+ZuHo66js56jkRTsbNxUhh8kMmNqRFXu99G0jHPmar/8gfo2IWK/uFyn7+8He+XDKPlFTcmP+qL4/nF0e02bA+90WvlIiU0nUdLLIx3OZ0pcApG9VpLpTmBki33tLhoIRxgFiaRjeOcM0oE8JhDIDDcpHulZgjgDGucAblOvQv2Pff7aVydRebON7J54vwdNx8MZiazqIRzhC6EMnAw9H9vs+7+iFIhyuIVk3xbEqfjs+8TNmog2PdsiHezCZDC8m5AtoBdnzi+00amfk+MPtr66+LGkVwsIgzup4bSv7MHLiSEXTI8X7CN3IHa3qZ2pxTihCVlRqf2MmvaGV0ghqFMUYs266SapmQmk0xKAefx2yLcHy5RW1CMRGEAEAtSutAahQpnq9I2UoUCwEeXZVctGCDugdHfGrj2DrGwbmAhi8hhOsqhmFks5qW1fRcLru9u/Xgbe2wWjQMrQmLhfTS2s6TQRCNFgycEQZSWnPb9ZElJRgMejweVVUvqapXVX3X2xKJREdHB0KvF4Hq9amBznbTNNEFND4L0JiVNurrmf/HUCgR0k1pKgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lunch full screen\"\n        title=\"\"\n        src=\"/static/e0046107a5c07e5f011f9686ba208635/302a4/launchfullscreen.png\"\n        srcset=\"/static/e0046107a5c07e5f011f9686ba208635/01bf6/launchfullscreen.png 270w,\n/static/e0046107a5c07e5f011f9686ba208635/07484/launchfullscreen.png 540w,\n/static/e0046107a5c07e5f011f9686ba208635/302a4/launchfullscreen.png 1080w,\n/static/e0046107a5c07e5f011f9686ba208635/105d8/launchfullscreen.png 1170w\"\n        sizes=\"(max-width: 1080px) 100vw, 1080px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<blockquote>\n<h6 id=\"photo-from-chromium-blog\" style=\"position:relative;\"><a href=\"#photo-from-chromium-blog\" aria-label=\"photo from chromium blog permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>photo from Chromium Blog</h6>\n</blockquote>\n<p>The easiest way to create your application’s manifest file is to use some of the tools out there. The one I like is called <em>App Manifest Generator</em> and you can find it <a href=\"https://app-manifest.firebaseapp.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">here</a>.</p>\n<h2 id=\"a-idangularpwaaangular-and-pwa\" style=\"position:relative;\"><a href=\"#a-idangularpwaaangular-and-pwa\" aria-label=\"a idangularpwaaangular and pwa permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"angularpwa\"></a>Angular and PWA</h2>\n<p>With the introduction of <a href=\"https://angular.io/guide/service-worker-intro\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Angular Service Worker</a> after releasing their version <code class=\"language-text\">5.0.0</code> and the Angular CLI built-in PWA support, it’s now easier than ever to make your web application faster, more reliable, more engaging, and installable like a native one.</p>\n<p>Let’s see what they say about their new addition to the family:</p>\n<blockquote>\n<p>Angular’s service worker is designed to optimize the end user experience of using an application over a slow or unreliable network connection, while also minimizing the risks of serving outdated content.</p>\n</blockquote>\n<p>To use Angular service workers, you must have the following Angular and CLI versions:</p>\n<ul>\n<li>Angular 5.0.0 or later.</li>\n<li>Angular CLI 1.6.0 or later.</li>\n</ul>\n<h2 id=\"a-idngswaangular-service-worker-aka-ngsw\" style=\"position:relative;\"><a href=\"#a-idngswaangular-service-worker-aka-ngsw\" aria-label=\"a idngswaangular service worker aka ngsw permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"ngsw\"></a>Angular Service Worker aka (ngsw)</h2>\n<p>In previous versions of Angular, you had to use a third party library like <code class=\"language-text\">sw-precache</code> or <a href=\"https://developers.google.com/web/tools/workbox/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">workbox</a> to add a service worker to your application. <a href=\"https://angular.io/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Angular</a> team have spent a fair amount of time on creating their own service worker aka <code class=\"language-text\">ngsw</code> and adding the support to its CLI which you can add to your project with a couple of commands. The first one would be obviously to install it:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"26306436670968967000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install @angular/service-worker --save`, `26306436670968967000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> @angular/service-worker <span class=\"token parameter variable\">--save</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>At this point there would be two possible scenario, either you already have an Angular application and want to add PWA support to it, or you are creating a new application. I will go through the former in another post.</p>\n<p>In terms of the later, it would be very easy:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"10217034677970792000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`ng new angular-pwa-app --service-worker`, `10217034677970792000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\">ng new angular-pwa-app --service-worker</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>And you’ve got the basic generated for you to <em>progressify</em> your application. This means you now should have configuration file <code class=\"language-text\">ngsw-config.json</code> in your project which tell service worker what to do. You should also have the service worker itself <code class=\"language-text\">ngsw-worker.js</code> in your <code class=\"language-text\">node_module</code> folder.</p>\n<p>Here is what’s inside the configuration file:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"29974950373381290000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\r\n  &quot;index&quot;: &quot;/index.html&quot;,\r\n  &quot;assetGroups&quot;: [\r\n    {\r\n      &quot;name&quot;: &quot;app&quot;,\r\n      &quot;installMode&quot;: &quot;prefetch&quot;,\r\n      &quot;resources&quot;: {\r\n        &quot;files&quot;: [&quot;/favicon.ico&quot;, &quot;/index.html&quot;],\r\n        &quot;versionedFiles&quot;: [&quot;/*.bundle.css&quot;, &quot;/*.bundle.js&quot;, &quot;/*.chunk.js&quot;]\r\n      }\r\n    },\r\n    {\r\n      &quot;name&quot;: &quot;assets&quot;,\r\n      &quot;installMode&quot;: &quot;lazy&quot;,\r\n      &quot;updateMode&quot;: &quot;prefetch&quot;,\r\n      &quot;resources&quot;: {\r\n        &quot;files&quot;: [&quot;/assets/**&quot;]\r\n      }\r\n    }\r\n  ]\r\n}`, `29974950373381290000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\r\n  <span class=\"token property\">\"index\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/index.html\"</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"assetGroups\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n    <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"app\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"installMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"resources\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"files\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/favicon.ico\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"/index.html\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"versionedFiles\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/*.bundle.css\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"/*.bundle.js\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"/*.chunk.js\"</span><span class=\"token punctuation\">]</span>\r\n      <span class=\"token punctuation\">}</span>\r\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"assets\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"installMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"lazy\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"updateMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"resources\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"files\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/assets/**\"</span><span class=\"token punctuation\">]</span>\r\n      <span class=\"token punctuation\">}</span>\r\n    <span class=\"token punctuation\">}</span>\r\n  <span class=\"token punctuation\">]</span>\r\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>As you can see the most important part in this file is about caching mechanism and which files to cache for the application. We will go through these in details later.</p>\n<p>You will also have a flag set in your <code class=\"language-text\">.angular-cli.json</code>:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"80153842068469120000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`&quot;serviceWorker&quot;: true`, `80153842068469120000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token property\">\"serviceWorker\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>This flag will cause the production build to include a couple of extra files in the output dist folder:</p>\n<ul>\n<li>The Angular Service Worker file <code class=\"language-text\">ngsw-worker.js</code></li>\n<li>The runtime configuration of the Angular Service Worker <code class=\"language-text\">ngsw.json</code></li>\n</ul>\n<p>The last bit but also important is that it will add the <code class=\"language-text\">ServiceWorkerModule</code> to your <code class=\"language-text\">app.module.ts</code> file and configure it for production.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"12471027596756423000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`@NgModule({\r\n  declarations: [AppComponent],\r\n  imports: [\r\n    BrowserModule,\r\n    AppRoutingModule,\r\n    ServiceWorkerModule.register('/ngsw-worker.js', {\r\n      enabled: environment.production,\r\n    }),\r\n  ],\r\n  providers: [],\r\n  bootstrap: [AppComponent],\r\n})\r\nexport class AppModule {}`, `12471027596756423000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-typescript line-numbers\"><code class=\"language-typescript\"><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">NgModule</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\r\n  declarations<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>AppComponent<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n  imports<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n    BrowserModule<span class=\"token punctuation\">,</span>\r\n    AppRoutingModule<span class=\"token punctuation\">,</span>\r\n    ServiceWorkerModule<span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/ngsw-worker.js'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\r\n      enabled<span class=\"token operator\">:</span> environment<span class=\"token punctuation\">.</span>production<span class=\"token punctuation\">,</span>\r\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n  providers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n  bootstrap<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>AppComponent<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\r\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">AppModule</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>This module provides a couple of injectable services:</p>\n<ul>\n<li><code class=\"language-text\">SwUpdate</code> for managing application version updates</li>\n<li><code class=\"language-text\">SwPush</code> for doing server Web Push notifications\r\nYou won’t need to know what is inside the service worker file itself and you cannot change that either (it will be overwritten by each build), but I recommend you have a look at <a href=\"https://github.com/angular/angular/tree/master/packages/service-worker/worker/src\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">the code</a> to understand some of the techniques which is used, interesting stuff.</li>\n</ul>\n<blockquote>\n<p>This file has its own HTTP request so that the browser would know which if there is a newer version of the service worker available.</p>\n</blockquote>\n<p>The Angular Service Worker is nothing but a javascript file subscribing to service worker life cycles and handling different stuff in there.</p>\n<h2 id=\"a-idhoodaunder-the-hood\" style=\"position:relative;\"><a href=\"#a-idhoodaunder-the-hood\" aria-label=\"a idhoodaunder the hood permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"hood\"></a>Under the hood</h2>\n<p>The Angular Service Worker can cache all sorts of files/content in the browser’s <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Cache Storage</a>. It doesn’t use the normal caching mechanism which is used in the standard browsers called <code class=\"language-text\">cache-control</code> so it can be used in conjunction with that.</p>\n<p>There were two sections in the  <code class=\"language-text\">assetGroup</code> in configuration file’s, the <code class=\"language-text\">app</code> and <code class=\"language-text\">assets</code>. The former is used to let Angular Service Worker know what files need to be cached from application’s main files.</p>\n<p>This usually include <code class=\"language-text\">index.html</code>, css files and main bundles. The assets section is used for anything that doesn’t need to be loaded at first like images and other assets which would be loaded later on demand.</p>\n<p>There were also two different install/update mode mentioned in the configuration, <code class=\"language-text\">prefetch</code> and <code class=\"language-text\">lazy</code>.</p>\n<p><strong>Prefetch</strong> means that the files are cached ahead of time and <strong><em>lazy</em></strong> is used for on demand caching, meaning they will be cached on their first usage.</p>\n<p>OK let’s build the application and have a look at what will be generated in the dist folder:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"64196306026728340000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`ng build --prod`, `64196306026728340000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\">ng build <span class=\"token parameter variable\">--prod</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>If you now open your dist folder you will see something similar to this:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1016px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/54c1bc5923ebb74b7d31af0f3bdada4c/f4281/ngswpwadist.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 52.59259259259259%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABXUlEQVR42n1RWW6DQBTjJCFhXwYGhmEWZoEE0t6gH73/QeoEqUppVcmyrAcPbL8gKVoj3WQXf3sn3RjnBBMgysglrf9HEMbVICwbDVde2at2N6FnMS2EinNSR1nzjUtKDghOGf2c3WoX6e7arky4OKdp2YOBpOiAKGt3jfkrgjBpBl4KJZVduXTGr2KaR+3BsABTAIQy17IZYBVxvgHbhPF8lCOXHjvaXmEem2C73O287dr4DcthXP7IjGB1W9KBc+WUWUblSSfSkhaEFaQvGzDLqq4kDC2ek+pQGGn7oudc6GUQBva6QeOl3/iz7Zqygktp/F1oP0iLP+PBYW3n6LB8TkhFsm7gk9+kWSrKk6LJ6x4+87qD2G2nFW16UVP+Gjs4RRXTjGutzG1UDsWaeUNDk1/BSDE9L4860CU+FL74D0Ice/1g7k2ox0lQeMc1/KM5RHjc7KnROeaHzF9/bXiGu/elIgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"NGSW dist folder\"\n        title=\"\"\n        src=\"/static/54c1bc5923ebb74b7d31af0f3bdada4c/f4281/ngswpwadist.png\"\n        srcset=\"/static/54c1bc5923ebb74b7d31af0f3bdada4c/01bf6/ngswpwadist.png 270w,\n/static/54c1bc5923ebb74b7d31af0f3bdada4c/07484/ngswpwadist.png 540w,\n/static/54c1bc5923ebb74b7d31af0f3bdada4c/f4281/ngswpwadist.png 1016w\"\n        sizes=\"(max-width: 1016px) 100vw, 1016px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>We’ve talk’ed about the service worker itself, so let’s have a look at the <code class=\"language-text\">ngsw.json</code> to see how ‘the template helped to generate this file.</p>\n<p>This is the <em>runtime</em> configuration file that the Angular Service Worker will use and is built based on the <code class=\"language-text\">ngsw-config.json</code>.</p>\n<p>It contains all the information needed by the Angular Service Worker to know at runtime about which files it needs to cache, and when.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"73561728369443920000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\r\n  &quot;configVersion&quot;: 1,\r\n  &quot;index&quot;: &quot;/index.html&quot;,\r\n  &quot;assetGroups&quot;: [\r\n    {\r\n      &quot;name&quot;: &quot;app&quot;,\r\n      &quot;installMode&quot;: &quot;prefetch&quot;,\r\n      &quot;updateMode&quot;: &quot;prefetch&quot;,\r\n      &quot;urls&quot;: [\r\n        &quot;/favicon.ico&quot;,\r\n        &quot;/index.html&quot;,\r\n        &quot;/inline.5646543f86fbfdc19b11.bundle.js&quot;,\r\n        &quot;/main.3bb4e08c826e33bb0fca.bundle.js&quot;,\r\n        &quot;/polyfills.55440df0c9305462dd41.bundle.js&quot;,\r\n        &quot;/styles.1862c2c45c11dc3dbcf3.bundle.css&quot;\r\n      ],\r\n      &quot;patterns&quot;: []\r\n    },\r\n    {\r\n      &quot;name&quot;: &quot;assets&quot;,\r\n      &quot;installMode&quot;: &quot;lazy&quot;,\r\n      &quot;updateMode&quot;: &quot;prefetch&quot;,\r\n      &quot;urls&quot;: [],\r\n      &quot;patterns&quot;: []\r\n    }\r\n  ],\r\n  &quot;dataGroups&quot;: [],\r\n  &quot;hashTable&quot;: {\r\n    &quot;/inline.5646543f86fbfdc19b11.bundle.js&quot;: &quot;1028ce05cb8393bd53706064e3a8dc8f646c8039&quot;,\r\n    &quot;/main.3bb4e08c826e33bb0fca.bundle.js&quot;: &quot;ae15cc3875440d0185b46b4b73bfa731961872e0&quot;,\r\n    &quot;/polyfills.55440df0c9305462dd41.bundle.js&quot;: &quot;c3b13e2980f9515f4726fd2621440bd7068baa3b&quot;,\r\n    &quot;/styles.1862c2c45c11dc3dbcf3.bundle.css&quot;: &quot;3318b88e1200c77a5ff691c03ca5d5682a19b196&quot;,\r\n    &quot;/favicon.ico&quot;: &quot;84161b857f5c547e3699ddfbffc6d8d737542e01&quot;,\r\n    &quot;/index.html&quot;: &quot;cfdca0ab1cec8379bbbf8ce4af2eaa295a3f3827&quot;\r\n  }\r\n}`, `73561728369443920000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\r\n  <span class=\"token property\">\"configVersion\"</span><span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"index\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/index.html\"</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"assetGroups\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n    <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"app\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"installMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"updateMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"urls\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n        <span class=\"token string\">\"/favicon.ico\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"/index.html\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"/inline.5646543f86fbfdc19b11.bundle.js\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"/main.3bb4e08c826e33bb0fca.bundle.js\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"/polyfills.55440df0c9305462dd41.bundle.js\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"/styles.1862c2c45c11dc3dbcf3.bundle.css\"</span>\r\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"patterns\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\r\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"assets\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"installMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"lazy\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"updateMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"urls\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"patterns\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\r\n    <span class=\"token punctuation\">}</span>\r\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"dataGroups\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"hashTable\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"/inline.5646543f86fbfdc19b11.bundle.js\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"1028ce05cb8393bd53706064e3a8dc8f646c8039\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"/main.3bb4e08c826e33bb0fca.bundle.js\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ae15cc3875440d0185b46b4b73bfa731961872e0\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"/polyfills.55440df0c9305462dd41.bundle.js\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"c3b13e2980f9515f4726fd2621440bd7068baa3b\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"/styles.1862c2c45c11dc3dbcf3.bundle.css\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"3318b88e1200c77a5ff691c03ca5d5682a19b196\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"/favicon.ico\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"84161b857f5c547e3699ddfbffc6d8d737542e01\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"/index.html\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"cfdca0ab1cec8379bbbf8ce4af2eaa295a3f3827\"</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>First of all there is the assetGroup section which now contains all the required files with their bundle id’s included. Then you can see there is a hash table generated containing the file names and a hash value generated based on the name. This hash table is used for versioning purposes.</p>\n<p>Let’s say you update your main style file, what happens next is that it’s id will change. As a result the hash value will change and service worker knows then that it needs to be requested again instead of responding with the cached version.</p>\n<p>You might ask where that <code class=\"language-text\">manifest.json</code> came from. That is not something that Angular generates, it is the application manifest that you should generate and put in your project and then reference it in your <code class=\"language-text\">.angular-cli.json</code>.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"85453853140256300000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`&quot;apps&quot;: [\r\n    {\r\n      &quot;root&quot;: &quot;src&quot;,\r\n      &quot;outDir&quot;: &quot;dist&quot;,\r\n      &quot;assets&quot;: [\r\n        &quot;assets&quot;,\r\n        &quot;favicon.ico&quot;,\r\n        &quot;assets\\\\manifest.json&quot;\r\n      ],\r\n    }\r\n    ...\r\n]`, `85453853140256300000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token property\">\"apps\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n    <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"root\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"src\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"outDir\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"dist\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"assets\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n        <span class=\"token string\">\"assets\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"favicon.ico\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token string\">\"assets\\\\manifest.json\"</span>\r\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token punctuation\">}</span>\r\n    ...\r\n<span class=\"token punctuation\">]</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>If you want to run your application to see whether the service worker is registered or not, be mindful of the fact that you cannot do it in non prod mode and you will need a web server for it.</p>\n<p>You could use the CLI dev server but that is not also not recommended but also it won’t pass the security part of the audit step in lighthouse.</p>\n<p>The best and simplest option is to install the <a href=\"https://www.npmjs.com/package/http-server\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http-server</a> and use that, but first we need to install it globally:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"54691456750293190000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install -g http-server`, `54691456750293190000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> <span class=\"token parameter variable\">-g</span> http-server</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>Now let’s then go into the <code class=\"language-text\">dist</code> folder, and start the application in production mode:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"97559329076067250000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`cd dist\r\n\r\nhttp-server -c-1 .`, `97559329076067250000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token builtin class-name\">cd</span> dist\r\n\r\nhttp-server -c-1 <span class=\"token builtin class-name\">.</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span></span></pre></div>\n<p>At this point the server will be initialised and serving from this folder as base, you can browse\r\n<u>_<a href=\"http://127.0.0.1:8080_\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://127.0.0.1:8080_</a></u> or <u>_<a href=\"http://localhost:8080_\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://localhost:8080_</a></u> to see your application and run the audit to see what else is remaining.</p>\n<p>If you open the developer tools, you should see the service worker registered for your application by going into application tab:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 682px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/7dd2c124e0f90017f7eb4d8683f9b197/160a3/ngsw.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 44.44444444444444%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhElEQVR42mWQy6rWMBSF+/4PIIoggmNn3gaKlwPHiYr8ovRvtbe0adKmubZJ2/Tibg8o6Mcigw0ra+8VlBWuCYUnywpcN2leVDXNcsRYV2HMeYcxJnVNW379iX7lREjV970xRggRNDjHKLVGbN6u87AvdvPDvjprh5oQ2sC/1WDtvu/Lui7rth46gEnw4Fn86MX1yZvs6S19+Kq6/7K897x8/BqbwRFCGGNKyo5zpdSRpxUESqUg+TC/+4Lef87efkpvvha339nNt/bDpf34g7lxgvB+6LdtB51R2x+OEZh5W2vJZteDlumv5nnuuo61LaW0rrH3a8NHrudWTMp40o2tGIMoisPwmqRALpVW2txpWY4bvfewrdaWMJeUphN2mpfBTW7yftmDoijCMIyiqEBIncfcAeb9XHQaR2lUiipUUgKl846xRkhuxyVIkuRyucRxDIbemH/MgITCBCcUw/KoRBUmLYcuBmmmoGkaaBXOA4PW+n+zP5lOnHPjOA9uPfvafwN3I/VWLmPFEwAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"NGSW\"\n        title=\"\"\n        src=\"/static/7dd2c124e0f90017f7eb4d8683f9b197/160a3/ngsw.png\"\n        srcset=\"/static/7dd2c124e0f90017f7eb4d8683f9b197/01bf6/ngsw.png 270w,\n/static/7dd2c124e0f90017f7eb4d8683f9b197/07484/ngsw.png 540w,\n/static/7dd2c124e0f90017f7eb4d8683f9b197/160a3/ngsw.png 682w\"\n        sizes=\"(max-width: 682px) 100vw, 682px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>You can see what fi’les have been cached so far by opening the cache section:’</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1080px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/9838f150120a2dc207e69fbe40ee94b2/098c1/ngswcache.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 49.62962962962963%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkElEQVR42o1Qa3ObMBDk//+XziS2P7bThzPuJNiTcdqMsbERIDBIIATiISAFuuTxPaubY/VYbu8M13UppeUHqqrKhOBXqiTXZdZrheiaAlkkEY+CXMQpv/I4SFhoeJ53OFi+7xdFAXFdVyHLVhuyemCL+3RxnyBWZrraZssNvV07N7/jL7+8xZ2z2FAjiiLGmBACSgVxVbFEfjXd25/WzY/Dcm0v7+yHU/6Hduun6zeTPJLq+2MAsn6KDEJIEARxHCulZttlmec5cT2HkPPFAXFdL5NymibGE58GXd+H18jzKbbG5XLZ7/d4+iae66uy0V3TdrVu62aOpu1fhkm3PU7AkHCLQwPdHo9HVMOo3vRKFegc/pumaYEOS+tWt4iua98JVmuEYQglZHVd47XWGpNDI5xzmckiU3mSiETKWOZcFoKn4KLMmMCkDMdx/j4/W5ZFXwEj+B1cTK8YgWEYh3H8N84ZG+Rhmr/DMNvebrdoe7fbgZimCQ7z0ydgpGl6Op1QH5Ozbft8PoOgi8+I/wPQEybpDecThwAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Cache\"\n        title=\"\"\n        src=\"/static/9838f150120a2dc207e69fbe40ee94b2/302a4/ngswcache.png\"\n        srcset=\"/static/9838f150120a2dc207e69fbe40ee94b2/01bf6/ngswcache.png 270w,\n/static/9838f150120a2dc207e69fbe40ee94b2/07484/ngswcache.png 540w,\n/static/9838f150120a2dc207e69fbe40ee94b2/302a4/ngswcache.png 1080w,\n/static/9838f150120a2dc207e69fbe40ee94b2/098c1/ngswcache.png 1578w\"\n        sizes=\"(max-width: 1080px) 100vw, 1080px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>The Angular Serv’ice Worker will start serving the application files the next time you load the ‘page. If you hit refresh, you will notice that the application starts much faster this time (note that the first time the service worker gets registered, so caching starts after that).</p>\n<h2 id=\"a-idotherresourceaextending-the-configuration-caching-other-resources\" style=\"position:relative;\"><a href=\"#a-idotherresourceaextending-the-configuration-caching-other-resources\" aria-label=\"a idotherresourceaextending the configuration caching other resources permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"otherresource\"></a>Extending the configuration, caching other resources</h2>\n<p>When you are adding Angular Service Worker to your existing application or when you have a more complex project calling web API endpoints to get data or maybe you are including some static resources from an <a href=\"https://en.wikipedia.org/wiki/Content_delivery_network\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">external CDN</a>.</p>\n<p>You can cache those items easily by extending the configuration file. If you remember from the snippet above, there was an empty section called <code class=\"language-text\">dataGroups</code>.</p>\n<p>In this section you can easily add these kind of resources and set a whole bunch of caching configuration for them. So let’s see what is available.</p>\n<p>We start with fonts which are the most obvious case, this would also go in <code class=\"language-text\">assetGroup</code> section. I’m using google fonts but this can be from anywhere. All you need to do is to add the <code class=\"language-text\">urls</code> property in the assets with lazy install mode since you don’t want to slow down your application load time by loading the fonts first.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"85866578578738760000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\r\n  &quot;name&quot;: &quot;assets&quot;,\r\n  &quot;installMode&quot;: &quot;lazy&quot;,\r\n  &quot;updateMode&quot;: &quot;prefetch&quot;,\r\n  &quot;resources&quot;: {\r\n    &quot;files&quot;: [&quot;/assets/**&quot;],\r\n    &quot;urls&quot;: [&quot;https://fonts.googleapis.com/**&quot;]\r\n  }\r\n}`, `85866578578738760000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\r\n  <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"assets\"</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"installMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"lazy\"</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"updateMode\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"prefetch\"</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token property\">\"resources\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"files\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/assets/**\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"urls\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"https://fonts.googleapis.com/**\"</span><span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>This makes sure that any font (using wildcard) requested from <u><strong>fonts.googleapis.com</strong></u> will get cached and served from there on future requests.</p>\n<p>In terms of APIs the <code class=\"language-text\">dataGroup</code> comes to play. We will need to tell service worker which routes are to be used, the size of cached items, their life time in cache (expiry) and also time out if the network request fails.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"81125341996018730000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`&quot;dataGroups&quot;: [\r\n  {\r\n    &quot;name&quot;: &quot;books-api&quot;,\r\n    &quot;urls&quot;: [&quot;/books&quot;],\r\n    &quot;cacheConfig&quot;: {\r\n      &quot;strategy&quot;: &quot;freshness&quot;,\r\n      &quot;maxSize&quot;: 20,\r\n      &quot;maxAge&quot;: &quot;1h&quot;,\r\n      &quot;timeout&quot;: &quot;5s&quot;\r\n    }\r\n  },\r\n  {\r\n    &quot;name&quot;: &quot;book-api&quot;,\r\n    &quot;urls&quot;: [&quot;/book/:id&quot;],\r\n    &quot;cacheConfig&quot;: {\r\n      &quot;strategy&quot;: &quot;performance&quot;,\r\n      &quot;maxSize&quot;: 10,\r\n      &quot;maxAge&quot;: &quot;1d&quot;\r\n    }\r\n  }\r\n]`, `81125341996018730000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token property\">\"dataGroups\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"books-api\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"urls\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/books\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"cacheConfig\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"strategy\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"freshness\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"maxSize\"</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"maxAge\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"1h\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"timeout\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"5s\"</span>\r\n    <span class=\"token punctuation\">}</span>\r\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"book-api\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"urls\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/book/:id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"cacheConfig\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"strategy\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"performance\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"maxSize\"</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"maxAge\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"1d\"</span>\r\n    <span class=\"token punctuation\">}</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">]</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>The most important part of the above config is the strategy. There are currently two values available, <code class=\"language-text\">freshness</code> and <code class=\"language-text\">performance</code>. The first one is equal to <strong>network first</strong>, meaning the resource is requested from network first and if not available from cache.</p>\n<p>The second one is equal to <strong>cache first</strong> means it is returned from cache first and if it does not exist on cache it will be requested from network.</p>\n<p><strong>Max size</strong> determines for how many entries should be kept in cache, because we don’t want to take too much space in browser cache and cause problems. <strong>Max age</strong> is used to determine how long responses are allowed to remain in the cache before being considered invalid and evicted.</p>\n<p>You can have these suffixes:</p>\n<ul>\n<li><code class=\"language-text\">d</code>: days</li>\n<li><code class=\"language-text\">h</code>: hours</li>\n<li><code class=\"language-text\">m</code>: minutes</li>\n<li><code class=\"language-text\">s</code>: seconds</li>\n<li><code class=\"language-text\">u</code>: milliseconds\r\nAfter configuring these options you can now try to set your app to offline in dev tools and see what happens. It does feel good to see the application working without a network connection, doesn’t it.</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1080px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/1edcd115389080aac4faa53478eacd92/93df8/bookshelfpic.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 30%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAABCUlEQVR42k2QwUrEMBRF+6G6sqDouHBEEfEr3Lhw4a8MdqA7/QKhdgptpknTpEnT5E2r144OXsIjvHdPbpLo5CqJr5Oz2/XxYnV6sz6/Sx+f38qyYGwrRCNlI0TddZ21RmujtLHWOuf8rGhxn14+pPEyARkvX48uVk8v75/ZR1EUUkpgrneBKNBuFk3T9PWnqOFcSsNFZyz1bnTDVNcyz3POuTGGaAe3Ng4rhPAPnGFWbopNXm+ZlKJpuFISt8qyjDEG+Cds3BnTadW2Sg9h3GP7/Ehp3bZt3/ewzm+zSKiqSgihtQYL3zAMmGLkfRhnoUNEEXYYHypOQfIBxq/AhA5gVAq/Auy9/wYxVEgGeIPskgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Bookshelf\"\n        title=\"\"\n        src=\"/static/1edcd115389080aac4faa53478eacd92/302a4/bookshelfpic.png\"\n        srcset=\"/static/1edcd115389080aac4faa53478eacd92/01bf6/bookshelfpic.png 270w,\n/static/1edcd115389080aac4faa53478eacd92/07484/bookshelfpic.png 540w,\n/static/1edcd115389080aac4faa53478eacd92/302a4/bookshelfpic.png 1080w,\n/static/1edcd115389080aac4faa53478eacd92/0d292/bookshelfpic.png 1620w,\n/static/1edcd115389080aac4faa53478eacd92/b3608/bookshelfpic.png 2160w,\n/static/1edcd115389080aac4faa53478eacd92/93df8/bookshelfpic.png 3292w\"\n        sizes=\"(max-width: 1080px) 100vw, 1080px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"a-idpushapush-notifications\" style=\"position:relative;\"><a href=\"#a-idpushapush-notifications\" aria-label=\"a idpushapush notifications permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><a id=\"push\"></a>Push notification’s’</h2>\n<p>To me the most impressing feature coming with PWAs is the push notification. This somehow completes the native user experience alongside the <em>add to home screen</em> feature.</p>\n<p>The good news is that this is supported by default in Angular and you don’t need to put too much effort in getting it working. We will however need to follow some conventions to implement the functionality in our API (as its needed for this purpose).</p>\n<p>First thing we need to do is to subscribe to the notifications. We will need <code class=\"language-text\">SwPush</code> from the Angular Service Worker to implement this:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"6891012464594226000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import { SwPush } from '@angular/service-worker';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { Observable } from 'rxjs/Observable';\r\n...\r\n\r\n  constructor(private swPush: SwPush, private http: HttpClient) {}\r\n  VAPID_PUBLIC_KEY = 'your VAPID private key';\r\n  API_URL = 'the api endpoint which supports push notification';\r\n\r\n  subscribeToPush() {\r\n    // Requesting messaging service to subscribe current client (browser)\r\n    this.swPush.requestSubscription({\r\n      serverPublicKey: this.VAPID_PUBLIC_KEY\r\n    })\r\n      .then(pushSubscription =&gt; {\r\n\r\n        // Passing subscription object to our backend\r\n        this.addSubscriber(pushSubscription)\r\n          .subscribe(\r\n            res =&gt; {\r\n              console.log('[App] Add subscriber request answer', res);\r\n            },\r\n            err =&gt; {\r\n              console.log('[App] Add subscriber request failed', err);\r\n            })\r\n      })\r\n      .catch(err =&gt; {\r\n        ...\r\n      })\r\n  }\r\n\r\n  addSubscriber(subscription) {\r\n\r\n    const url = \\`\\${this.API_URL}\\`;\r\n    console.log('[Push Service] Adding subscriber')\r\n\r\n    let body = {\r\n      action: 'subscribing to the push endpoint',\r\n      subscription: subscription\r\n    }\r\n\r\n    return this.http\r\n      .post(url, body)\r\n      .catch(this.handleError);\r\n\r\n  }\r\n\r\n  private handleError(error: Response | any) {\r\n    let errMsg: string;\r\n    if (error instanceof Response) {\r\n      errMsg = \\`\\${error.statusText || 'Network error'}\\`;\r\n    } else {\r\n      errMsg = error.message ? error.message : error.toString();\r\n    }\r\n    return Observable.throw(errMsg);\r\n  }`, `6891012464594226000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-typescript line-numbers\"><code class=\"language-typescript\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> SwPush <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@angular/service-worker'</span><span class=\"token punctuation\">;</span>\r\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> HttpClient <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@angular/common/http'</span><span class=\"token punctuation\">;</span>\r\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Observable <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'rxjs/Observable'</span><span class=\"token punctuation\">;</span>\r\n<span class=\"token operator\">...</span>\r\n\r\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">private</span> swPush<span class=\"token operator\">:</span> SwPush<span class=\"token punctuation\">,</span> <span class=\"token keyword\">private</span> http<span class=\"token operator\">:</span> HttpClient<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\r\n  <span class=\"token constant\">VAPID_PUBLIC_KEY</span> <span class=\"token operator\">=</span> <span class=\"token string\">'your VAPID private key'</span><span class=\"token punctuation\">;</span>\r\n  <span class=\"token constant\">API_URL</span> <span class=\"token operator\">=</span> <span class=\"token string\">'the api endpoint which supports push notification'</span><span class=\"token punctuation\">;</span>\r\n\r\n  <span class=\"token function\">subscribeToPush</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token comment\">// Requesting messaging service to subscribe current client (browser)</span>\r\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>swPush<span class=\"token punctuation\">.</span><span class=\"token function\">requestSubscription</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\r\n      serverPublicKey<span class=\"token operator\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token constant\">VAPID_PUBLIC_KEY</span>\r\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\r\n      <span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span>pushSubscription <span class=\"token operator\">=</span><span class=\"token operator\">&amp;</span>gt<span class=\"token punctuation\">;</span> <span class=\"token punctuation\">{</span>\r\n\r\n        <span class=\"token comment\">// Passing subscription object to our backend</span>\r\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">addSubscriber</span><span class=\"token punctuation\">(</span>pushSubscription<span class=\"token punctuation\">)</span>\r\n          <span class=\"token punctuation\">.</span><span class=\"token function\">subscribe</span><span class=\"token punctuation\">(</span>\r\n            res <span class=\"token operator\">=</span><span class=\"token operator\">&amp;</span>gt<span class=\"token punctuation\">;</span> <span class=\"token punctuation\">{</span>\r\n              <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[App] Add subscriber request answer'</span><span class=\"token punctuation\">,</span> res<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\r\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n            err <span class=\"token operator\">=</span><span class=\"token operator\">&amp;</span>gt<span class=\"token punctuation\">;</span> <span class=\"token punctuation\">{</span>\r\n              <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[App] Add subscriber request failed'</span><span class=\"token punctuation\">,</span> err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\r\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\r\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\r\n      <span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span>err <span class=\"token operator\">=</span><span class=\"token operator\">&amp;</span>gt<span class=\"token punctuation\">;</span> <span class=\"token punctuation\">{</span>\r\n        <span class=\"token operator\">...</span>\r\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\r\n  <span class=\"token punctuation\">}</span>\r\n\r\n  <span class=\"token function\">addSubscriber</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\r\n\r\n    <span class=\"token keyword\">const</span> url <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token constant\">API_URL</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\r\n    <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'[Push Service] Adding subscriber'</span><span class=\"token punctuation\">)</span>\r\n\r\n    <span class=\"token keyword\">let</span> body <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\r\n      action<span class=\"token operator\">:</span> <span class=\"token string\">'subscribing to the push endpoint'</span><span class=\"token punctuation\">,</span>\r\n      subscription<span class=\"token operator\">:</span> subscription\r\n    <span class=\"token punctuation\">}</span>\r\n\r\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>http\r\n      <span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> body<span class=\"token punctuation\">)</span>\r\n      <span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>handleError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\r\n\r\n  <span class=\"token punctuation\">}</span>\r\n\r\n  <span class=\"token keyword\">private</span> <span class=\"token function\">handleError</span><span class=\"token punctuation\">(</span>error<span class=\"token operator\">:</span> Response <span class=\"token operator\">|</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token keyword\">let</span> errMsg<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\r\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">Response</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\r\n      errMsg <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>error<span class=\"token punctuation\">.</span>statusText <span class=\"token operator\">||</span> <span class=\"token string\">'Network error'</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\r\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\r\n      errMsg <span class=\"token operator\">=</span> error<span class=\"token punctuation\">.</span>message <span class=\"token operator\">?</span> error<span class=\"token punctuation\">.</span>message <span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\r\n    <span class=\"token punctuation\">}</span>\r\n    <span class=\"token keyword\">return</span> Observable<span class=\"token punctuation\">.</span><span class=\"token function\">throw</span><span class=\"token punctuation\">(</span>errMsg<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\r\n  <span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Let me break this down and explain it step by step.</p>\n<p>The first step is to subscribe to your web push endpoint (this is not your back-end server) which is responsible for sending push notifications. This is like establishing a connection with a server and keeping that open so they can send us the notifications later through that (of course this was just an example and not reality).</p>\n<p>To do this we need to have a valid <a href=\"https://tools.ietf.org/html/draft-thomson-webpush-vapid-02\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Voluntary Application Server Identification</a> (VAPID) public key from our sever which <code class=\"language-text\">ngsw</code> will use to subscribe to the push endpoint. If you are wondering about these concepts and don’t have enough background refer to <a href=\"https://tools.ietf.org/html/draft-ietf-webpush-protocol\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Web Push Protocol</a> specifications.</p>\n<p>In short the whole process looks like this:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 638px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/4474adbfe08bef3a017ae6d85efad1e6/8608d/webpush.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 41.85185185185185%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3QUD/8QAFRABAQAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCb//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABYQAQEBAAAAAAAAAAAAAAAAAAABIf/aAAgBAQABPyE2I//aAAwDAQACAAMAAAAQ8A//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEAAgMAAAAAAAAAAAAAAAABABEhMUH/2gAIAQEAAT8QDzFvJoAC3NE0n//Z'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"WebPush\"\n        title=\"\"\n        src=\"/static/4474adbfe08bef3a017ae6d85efad1e6/8608d/webpush.jpg\"\n        srcset=\"/static/4474adbfe08bef3a017ae6d85efad1e6/6f81f/webpush.jpg 270w,\n/static/4474adbfe08bef3a017ae6d85efad1e6/09d21/webpush.jpg 540w,\n/static/4474adbfe08bef3a017ae6d85efad1e6/8608d/webpush.jpg 638w\"\n        sizes=\"(max-width: 638px) 100vw, 638px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>As soon as you subscribe, you will start getting notifications. At this point I haven’t implemented the push notification in the demo application, but I will add it in my next blog post.</p>\n<h4 id=\"considerations-with-push-notifications-using-ngsw\" style=\"position:relative;\"><a href=\"#considerations-with-push-notifications-using-ngsw\" aria-label=\"considerations with push notifications using ngsw permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Considerations with push notifications using ngsw</h4>\n<ul>\n<li>You nee to send notification data straight away with the send notification request as payload (NGSW can’t request this payload later, which is possible in general).</li>\n<li>You have to send this data object in the <code class=\"language-text\">notification</code> property of the payload.</li>\n</ul>\n<p>A normal payload structure will look like:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"19583832293630386000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\r\n  &quot;notification&quot;: {\r\n    &quot;title&quot;: &quot;The push notification title&quot;,\r\n    &quot;actions&quot;: [\r\n      {\r\n        &quot;action&quot;: &quot;actionOne&quot;,\r\n        &quot;title&quot;: &quot;Action One&quot;\r\n      }\r\n    ],\r\n    &quot;body&quot;: &quot;The is the body which will be shown on notification&quot;,\r\n    &quot;dir&quot;: &quot;auto&quot;,\r\n    &quot;icon&quot;: &quot;path to icon&quot;,\r\n    &quot;badge&quot;: &quot;path to badge&quot;,\r\n    &quot;lang&quot;: &quot;en&quot;,\r\n    &quot;renotify&quot;: true,\r\n    &quot;requireInteraction&quot;: true,\r\n    &quot;tag&quot;: 926796012340920300,\r\n    &quot;vibrate&quot;: [300, 100, 400],\r\n    &quot;data&quot;: {\r\n      // this object can contain arbitrary info\r\n    }\r\n  }\r\n}`, `19583832293630386000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\r\n  <span class=\"token property\">\"notification\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"The push notification title\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"actions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"action\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"actionOne\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"title\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Action One\"</span>\r\n      <span class=\"token punctuation\">}</span>\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"body\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"The is the body which will be shown on notification\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"dir\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"auto\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"icon\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"path to icon\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"badge\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"path to badge\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"lang\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"en\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"renotify\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"requireInteraction\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"tag\"</span><span class=\"token operator\">:</span> <span class=\"token number\">926796012340920300</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"vibrate\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token number\">300</span><span class=\"token punctuation\">,</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span> <span class=\"token number\">400</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"data\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n      <span class=\"token comment\">// this object can contain arbitrary info</span>\r\n    <span class=\"token punctuation\">}</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Hope this article has helped you to get started with PWAs using Angular. Don’t forget to spread the love fellas.</p>","timeToRead":14,"frontmatter":{"title":"Angular and PWA, the life ahead of us","unformattedDate":"2018-02-09T00:00:00.000Z","date":"Feb 09, 2018","path":"angular-pwa/","author":"Yaser Adel Mehraban","tags":["angular","application manifest","pwa","progressive web apps","service worker"],"thumbnail":{"childImageSharp":{"gatsbyImageData":{"layout":"constrained","placeholder":{"fallback":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABb0lEQVR42mP4TwFggFD/wADORmbcvn17y+ZNrW2tNVWV7Z1dW7dte/bsGUSWAY/BYDP+NU1ZEtu6NShvhmvtqlmbTnYsObBy3RagxJ8/f0Ganz59eh4M7t+/f/fu3ZMnT967d+/z589gE/6mVU3hDVzhErkgMbjfpXCbfOrq+Ss2IDQDNZw+fXrv3r27d+++ePHiqVOnNm7cCNQP1vw7vWqSTOiys/ZRv8oq+xwzJLI2LFi6BqH56tWrR44cuXTp0r59+7Zt23bz5k1oKIBtzm+cIRW7dnN0zffswobEHsWi7SjO/vDhA9Cejx8/vnv37tGjR69fv4YFIYgu71oonbXNJX9dWli3Y+Mh48rty9dvQ2jGGWBgzaU9iyxq9wdOPe87+3rigutOLXuXr9+O0PwPAyBrLutdbFR9IGfhpcIll/NW3rZrO7Biw3bCNoNU/P61Yu3mM/fev/3048OXH68+/zl06eH2nbtBgfH3LwAqePiaTqzVygAAAABJRU5ErkJggg=="},"backgroundColor":"transparent","images":{"fallback":{"src":"/static/7460e741efcd94667a1a46875a8e7722/c7960/angularpwa.png","srcSet":"/static/7460e741efcd94667a1a46875a8e7722/c7960/angularpwa.png 700w","sizes":"(min-width: 700px) 700px, 100vw"},"sources":[{"srcSet":"/static/7460e741efcd94667a1a46875a8e7722/02196/angularpwa.webp 700w","type":"image/webp","sizes":"(min-width: 700px) 700px, 100vw"}]},"width":700,"height":350}}}}}}]}},"pageContext":{"tag":"progressive web apps"}},"staticQueryHashes":[],"slicesMap":{}}